/*
 * Fab's BoomBox Version 1.0
 * Copyright(c) 2009, Owl Watch Consulting Serivces, LLC
 * support@owlwatch.com
 * 
 * MIT License
 */

/**
 * @class Fabs.boombox.Player
 * @extends Ext.util.Observable
 * A class that represents the underlying music player.
 * @constructor
 * @param {Object} config The config object
 */
Fabs.boombox.Player = Ext.extend( Ext.util.Observable, {
    
	/**
	 * Read-only. Array of playlists
	 * @var playlists
	 * @type Array
	 */
	
	/**
	 * Read-only. Index of current playing track in history
	 * @type Array
	 */
	index : -1,
	
	/**
	 * Read-only. History of tracks played
	 * @var history
	 * @type Array
	 */
	
	activePlaylistIndex : -1,
	
	/**
     * @cfg {Boolean} autoPlay
     * Automatically start tracks when added (defaults to false)
     */
	autoPlay : false,
	
	/**
     * @cfg {Boolean} shuffle
     * Enable shuffled play (defaults to false)
     */
	shuffle : false,
	
	/**
	 * @cfg {Number} volume
	 * The initial volume level (Defaults to 80)
	 */
	volume : 80,
	
	// private
	nextTrack : null,
	
	/**
	 * Read-only. The reference to the currently playing track
	 * @type Fabs.boombox.Track
	 */
	currentTrack : null,
	
	// private
	autoPlayFlag : false,
    
    constructor : function(config){
		
		this.playlists = [];
		this.history = [];
		
		this.currentTrack = null;
		this.autoPlayFlag = false;
		
        this.addEvents({
			/**
             * @event play
             * Fires when the state of player is playing (a track is played or resumed)
             * @param {Player} this
             */
            'play'        		:true,
			
			/**
             * @event pause
             * Fires when the player is paused
             * @param {Player} this
             */
            'pause'             :true,
			
			/**
             * @event statechange
             * Fires when the player's state is altered (playing/paused)
             * @param {Player} this
             */
			'statechange'		:true,
			
			/**
			 * @event trackchange
			 * Fires when a track is loaded / added to the player
			 * @param {Player} this
			 * @param {Track} track
			 */
			
			/**
             * @event play
             * Fires when a playlist is added to the player
             * @param {Playlist} playlist
             */
			'playlistadded'		:true,
			
			/**
             * @event trackloaderror
             * Fires when a track fails to load
             * @param {Player} this
             * @param {Track} track
             */
			'trackloaderror'		:true
        });
        
        Ext.apply(this,config);
        
        this.init();
		
		Fabs.boombox.Player.superclass.constructor.call(this);
		
		this.history = [];
		
		this.on('playlistadded', this.onPlaylistAdded, this);
		
		if( this.playlist ){
			this.addPlaylist(this.playlist);
			delete this.playlist;
		}
		else{
			var playlist = new Fabs.boombox.Playlist(Ext.apply(this.playlistCfg||{},{
				autoLoad				:this.autoLoad
			}));
			this.addPlaylist(playlist);
			if (this.tracks) {
				playlist.addTracks(this.tracks.slice(0));
				delete this.tracks;
			}
        }
		
    },
	/**
     * Add a playlist to the player
     * @param {Playlist} playlist This is a valid playlist object
     */
	addPlaylist : function(playlist){
		this.playlists[this.playlists.length] = playlist;
		playlist.on('trackadded', this.onPlaylistTrackAdded, this);
		playlist.on('trackplay', this.onPlaylistTrackPlay, this);
		playlist.on('trackstop', this.onPlaylistTrackStop, this);
		playlist.on('trackpause', this.onPlaylistTrackPause, this);
		playlist.on('trackresume', this.onPlaylistTrackResume, this);
		playlist.on('trackload', this.onPlaylistTrackLoad, this);
		playlist.on('trackfinish', this.onPlaylistTrackFinish, this);
		playlist.on('trackloaderror', this.onPlaylistTrackLoadError, this);
		this.fireEvent('playlistadded', playlist);
	},
	
	// private
	onPlaylistTrackPlay : function(track){
		if( !this.currentTrack || track != this.currentTrack ){
			if( this.currentTrack ){
				this.currentTrack.stop();
			}
			this.currentTrack = track;
			this.fireEvent('statechange', this);
			this.fireEvent('trackchange', this, track);
		}
		if( track.id3 ){
			this.updateTrackInfo(track);
		}
		else{
			//Ext.fly('track').update('Loading ID3...');
			track.on('id3', this.updateTrackInfo, this);
		}
		track.setVolume(this.volume);
		this.fireEvent('statechange', this);
	},
	
	// private
	onPlaylistTrackPause : function(track){
		this.fireEvent('statechange', this);
	},
	
	// private
	onPlaylistTrackStop: function(track){
		this.fireEvent('statechange', this);
	},
	
	// private
	onPlaylistTrackResume : function(track){
		this.autoPlayFlag = false;
		track.setVolume(this.volume);
		this.fireEvent('statechange', this);
	},
	
	// private - for debugging right now
	updateTrackInfo : function(track){
		// Ext.fly('track').update(track.id3.track+'. '+track.id3.artist+' - '+track.id3.songname);
	},
	
	// private
	onPlaylistTrackFinish : function(){
		this.next();
		this.fireEvent('statechange', this);
	},
	
	// private
	onPlaylistAdded : function(playlist){
		if( this.activePlaylistIndex == -1 ){
			this.activePlaylistIndex = 0;
		}
	},
	
	// private
	onPlaylistTrackAdded : function(track, isLast, playlist){
		/*
		if( !this.isPlaying() && this.autoPlay && !this.autoPlayFlag){
			this.autoPlayFlag = true;
			track.play();
		}
		*/
		if( isLast && !this.isPlaying() && this.autoPlay ){
			this.getNextTrack().play();
			this.autoPlay = false;
		}
	},
	
	// private
	onPlaylistTrackLoadError : function(track){
		this.fireEvent('trackloaderror', this, this.currentTrack);
		this.currentTrack = null;
		this.next();
		this.fireEvent('statechange', this);
	},
	
	/**
     * Play the current track
     * @param {Track} track (optional) A reference to a track to play
     */
	play : function(track){
		if( !this.currentTrack && !track ){
			this.updateCurrentTrack();
		}
		if( track && track != this.currentTrack ){
			if( this.currentTrack ){
				this.currentTrack.stop();
			}
			this.index = this.history.length;
			this.history[this.index] = this.currentTrack = track;
			this.fireEvent("trackchange", this, track);
		}
		if( this.currentTrack ){
			this.currentTrack.play();
			this.playing = true;
		}
	},
	
	/**
	 * Toggle the play state of the current track, if available
	 */
	togglePlay : function(){
		if( this.isPlaying() ){
			this.pause();
		}
		else{
			this.play();
		}
	},
	
	/**
	 * Toggle the shuffle state of the player
	 */
	toggleShuffle : function(){
		this.shuffle = !this.shuffle;
		this.fireEvent('statechange',this);
	},
	
	/**
     * Advance to the next track
     */
	next : function(){
		this.updateCurrentTrack();
		if( this.currentTrack ){
			this.currentTrack.play();
		}
	},
	
	/**
     * Play the previous track in history
     */
	prev : function(){
		if( this.index > 0 && this.index - 1  < this.history.length ){
			if( this.currentTrack ){
				this.currentTrack.stop();
			}
			this.index--;
			this.currentTrack = this.history[this.index];
			this.fireEvent('trackchange', this, this.currentTrack );
			this.currentTrack.play();
		}
	},
	
	// private
	updateCurrentTrack : function(){
		if( this.currentTrack ){
			/**
			 * Need to destroy the sound object here.
			 */
			this.currentTrack.stop();
			// this.currentTrack.destroySoundObject();
		}
		if( this.index < this.history.length-1 && this.index > -1){
			this.index++;
			this.currentTrack = this.history[this.index];
		}
		else{
			this.index = this.history.length;
			this.history[this.index] = this.currentTrack = this.getNextTrack();
		}
		if( this.currentTrack ){
			this.fireEvent('trackchange', this, this.currentTrack );
		}
	},
	
	// private
	getNextTrack : function(){
		var pl = this.getPlaylist();
		return pl ? pl.getNext(this.shuffle) : null;
	},
	
	/**
     * Returns whether or not this player is currently playing
     * @return {Boolean} playing
     */
	isPlaying : function(){
		return this.currentTrack && this.currentTrack.isPlaying();
	},
	
	/**
	 * Pause the player's current track
	 */
	pause : function(){
		if( this.isPlaying() ){
			this.currentTrack.pause();
			this.playing = false;
		}
	},
	
	/**
     * Stop the player (moves the cursor to the beginning of the track)
     */
	stop : function(){
		if( this.currentTrack ){
			this.currentTrack.stop();
		}
	},
	
	/**
     * Get the currently loaded track
     * @return {Track} currentTrack
     */
	getCurrentTrack : function(){
		return this.currentTrack;
	},
    
	/**
     * Reset the player
     */
    reset : function(){
		if( this.currentTrack ){
			this.currentTrack.stop();
			// maybe we should destroy this.
		}
		this.currentTrack = null;
		this.playlists = [];
        this.history = [];
		this.index = -1;
    },
	
	/**
	 * Get the current playlist
	 * @return {Playlist}
	 */
	getPlaylist : function(){
		return this.playlists[this.activePlaylistIndex] || null;
	},
	
	/**
	 * Seek the current track by percentage.
	 * @param {Number} percentage A number between 0 and 100
	 */
	seek : function(p){
		if( this.currentTrack ){
			this.currentTrack.setProgressByPercent(p);
		}
	},
    
	// private
    init : function(){
        this.reset();
    },
	
	/**
     * Set the volume of the player
     * @param {Number} level A number between 0 and 100 that specifies the volume level
     */
	setVolume : function(level){
		if( level > 100 ){
			level = 100;
		}
		else if( level < 0 ){
			level = 0;
		}
		this.volume = level;
		if( this.currentTrack){
			this.currentTrack.setVolume(this.volume);
		}
	}
    
});